%% @author Hee Yeon Cho <aposto@nexon.co.kr>
%% @copyright 2009 Nexon Corporation.

%% @doc 
%%      
	
-module(savage_match).
-behaviour(gen_server).

-author('aposto@nexon.co.kr').

-include_lib("stdlib/include/qlc.hrl").
-include("engine.hrl").
-include("game.hrl").

-define(NOTEST, true).
-include_lib("eunit/include/eunit.hrl").

%% API
-export([start/0, start_link/0, stop/0, dev_start/0]).

-export([create/1, join/2, list/1, quit/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
		 terminate/2, code_change/3]).
 
-record(state, {game_tab, userdata}).

%% External functions

%% c(d_matchman, [debug_info, {d,'TEST'}]). 
%%  eunit:test(d_matchman, [verbose]).

%% @spec test() -> ok
%% @doc Run all of the tests.
test_code(#state{userdata=S2}) ->
	io:format("S2 => ~p~n", [S2]),
	ok.
	
dev_start() ->
	S = #state{game_tab=99},
	io:format("S => ~p~n", [S]),
	S1 = S#state{userdata=#match{}},
	io:format("S1 => ~p~n", [S1]),
	test_code(S1),
 
	
	stop(),
	%up_to_date = make:all([load, debug_info]),
	io:format("dev_start 1"),
	start_link(),
	timer:sleep(100),
	?MODULE:create("A1"),
	?MODULE:create("A2"),
	?MODULE:create("A3"),
	?MODULE:create("A4"),
	?MODULE:list("A"),
	
	?MODULE:quit(2),
	?MODULE:quit(3),
	
	?MODULE:create("A5"),
	
	?MODULE:quit(2),
	?MODULE:quit(1),
	?MODULE:create("A6"),
	?MODULE:create("A7"),
	?MODULE:create("A8"),
	?MODULE:create("A9"),
	
	?MODULE:list("A"),
	ok.
	
 
start() ->
	gen_server:start({local, ?MODULE}, ?MODULE, [], []).		

start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).	

stop() ->
	gen_server:cast( ?MODULE, stop ).
	
init([]) ->
	process_flag(trap_exit, true),
	error_logger:info_msg("~p starting~n", [?MODULE]),
	MatTid = ets:new( game_db, [ protected, ordered_set, named_table, {keypos, #match.no} ] ),	
	io:format("#start match db ~p~n", [MatTid]),	
	{ok, #state{game_tab=MatTid}}.

%% 
create(User) -> gen_server:cast( ?MODULE, {create, User} ).
	
join(User, No) -> gen_server:cast( ?MODULE, {join, User, No} ).

quit(User) -> gen_server:cast( ?MODULE, {quit, User} ).

list(User) -> gen_server:cast( ?MODULE, {list, User} ).
	
find_empty_index(C, Index) -> 		
	case qlc:next_answers(C, 1) of
		[] -> Index;
		[#match{no=No}] when Index < No -> Index;
		Any -> find_empty_index(C, Index+1)
	end.
		
	
new_match_index(Tab) ->
	%C = qlc:cursor( qlc:q( [X || X <- ets:table(Tab), {unique,true}] ) ),	
	C = qlc:cursor( ets:table(Tab) ),	
	NewIndex = find_empty_index(C, 1),	
	qlc:delete_cursor(C),
	NewIndex.
	
	
%% Handle Message
handle_call(_Request, _From, State) ->
	{reply, ok, State}.
	  
handle_cast( {create, User}, State ) ->	
	No = new_match_index( State#state.game_tab ),
	NewGame = #match{no=No, owner=User},
	ets:insert_new( State#state.game_tab, NewGame ),
	io:format("#create game ~p~n", [User]),
	{noreply, State};
	
handle_cast( {join, User, No}, State ) ->
	io:format("#join game ~p:~p~n", [User,No]),
	{noreply, State};

handle_cast( {quit, User}, State ) ->
	io:format("#quit game ~p~n", [User]),
	No = User,
	ets:delete( State#state.game_tab, No ),
	{noreply, State};

handle_cast( {list, User}, State ) ->
	Games = ets:tab2list( State#state.game_tab ),
	io:format("#list game ~p:~p~n", [User, Games]),
	{noreply, State};
		
handle_cast(stop, State) -> {stop, normal, State};
handle_cast(_Msg, State) -> {noreply, State}.

handle_info(Msg, State) ->
	error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
	{noreply, State}.

terminate(Reason, State) ->	
	io:format("#terminate ~p:~p~n", [ Reason, State#state.game_tab]),
	ets:delete(State#state.game_tab),
	ok.

code_change(_OldVersion, State, _Extra) -> {ok, State}.


find_empty_no(L) -> 
	find_empty_no(L, 1).	
find_empty_no([], I) -> 
	I;
find_empty_no([H|T], I) when I < H ->
	I;
find_empty_no([H|T], I) ->
	find_empty_no(T, I+1).	
					
 						

%% Internal functions
 

%% @spec test() -> integer() | ok | undefined | error
%% @doc Run all of the tests.
handle_test() ->
	[	 
		?assertEqual(1, find_empty_no([])),
		?assertEqual(1, find_empty_no([3,4,5])),
		?assertEqual(3, find_empty_no([1,2])),
		?assertEqual(4, find_empty_no([1,2,3,5])),
		?assertEqual(5, find_empty_no([1,2,3,4])),
		
		?assertNot(2 == find_empty_no([])),
		?assertNot(5 == find_empty_no([1,2,3])),
		?assertNot(4 == find_empty_no([1,3]))
		
	].

test_setup() -> 
	%?debugMsg("test_setup"),
	ets:new( game_db, [ public, ordered_set, named_table, {keypos, #match.no} ] ).
	
test_teardown(Arg) ->
	%?debugFmt("test_teardown ~p", [Arg]),
	ets:delete( game_db ).	

ets_find_index() ->	
	?debugMsg("ets_find_index_test_"),
			
	ets:insert_new(game_db, #match{no=5}),
	?assertEqual(1, new_match_index(game_db) ),
	
	ets:insert_new(game_db, #match{no=2}),
	ets:insert_new(game_db, #match{no=3}),
	ets:insert_new(game_db, #match{no=1}),
	?assertEqual(4, new_match_index(game_db) ),
	ets:insert_new(game_db, #match{no=4}),
	?assertEqual(6, new_match_index(game_db) ),
	
	ets:delete(game_db, 4),
	?assertEqual(4, new_match_index(game_db) ),
	
	ok.
	
ets_find_index_s() ->	
	ets:delete_all_objects(game_db),
	lists:foreach( fun(I) ->	ets:insert_new(game_db, #match{no=I}) end,
			lists:seq(1, 100) ),
	ets:delete(game_db, 77), 
	ets:delete(game_db, 66),
	ets:delete(game_db, 89),
	ets:delete(game_db, 75),
	?assertEqual(66, new_match_index(game_db) ),
	ok.	
		
all_tests_test_() ->
	{setup, 
		fun test_setup/0,
		fun test_teardown/1,
		[  
			?_test( ets_find_index() ),
			?_test( ets_find_index_s() )
		]}. 
